package qgs.zisu.javaconcurrency.javaconcurrency.chapter9_ThreadCommunication;

import java.util.stream.Stream;

/**
 * @Author: 蔡翔
 * @Date: 2019/10/13 20:36
 * @Version 1.0
 *
 * 多个生产者和 消费者  在多线程条件下 ，保持消费合理
 *
 * 查看是否有死锁；； cmd=》jps=》jstack 6（你所要查看主线程的id）
 */
public class ProductConsumerVersion3 {
    private int i = 0;
    // 这里的LOCK 就是monitor 对象监视器的作用。
    final private Object LOCK = new Object();

    //volatile 是共享变量
    //共享变量：共享变量是指可以同时被多个线程访问的变量，共享变量是被存放在堆里面，所有的方法内临时变量都不是共享变量。
    private volatile boolean isProduced = false;

    public void produce(){
        synchronized (LOCK){
            while(isProduced){
                // 如果已近被生产过了，别人还没有消费，那就不要生产了
                try {
                    //这个线程会一直等待，直到别的线程影响了它。
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            i++;
            System.out.println("p->"+i);
            // 我这里已经生产好了，通知“别的线程去消费”
            LOCK.notify();
            isProduced = true;

        }
    }

    public void consume(){
        synchronized (LOCK){
            while (!isProduced){
                //如果别人没有生产数据（即现在没有数据），那我得通过while循环先等着
                try {
                    //没有生产，那么我现在得等，让锁等
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("C->"+i);
            // 通知它，我已近消费了。可以再次生产了。
            // 如果这里改成 notifyAll(): 就会唤醒多余的线程，这样就会造成生产者多余的消费。
            LOCK.notify();
            isProduced = false;
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion2 pc = new ProduceConsumerVersion2();
        Stream.of("P1","P2").forEach(n->
                new Thread(){
                    @Override
                    public void run(){
                        while (true){
                            pc.produce();
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }.start()
        );

        Stream.of("C1","C2").forEach(n->

                new Thread("Consumer"){
                    @Override
                    public void run(){
                        while (true){
                            pc.consume();
                            try {
                                Thread.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }.start()
        );



        /**
         * 结果：
         p->1
         C->1
         p->2
         C->2
         p->3
         *
         * 系统之所以停住: 是因为多个生产者线程都wait 了，但是消费者只唤醒唤醒了一个，下一个生产者生产的时候 检查到还是wait，所以就假死了。
         * * */
    }
}
